/*-------------------------------------------------------------------------
 *
 * psqlscan_int.h
 *	  词法扫描器内部声明
 *
 * 此文件声明了由psqlscan.l使用的PsqlScanStateData结构，并被与它兼容的其他词法分析器共享，
 * 如psqlscanslash.l。
 *
 * 此代码的一个困难方面是我们需要在不是ASCII安全的多字节编码中工作。
 * “安全”编码是指每个多字节字符的每个字节都设置高位（其值>= 0x80）。因为我们的所有词法规则
 * 将所有高位集的字符视为相同，所以我们实际上不需要关心这样的字节是否是序列的一部分。
 * 在“不安全”编码中，我们仍然希望多字节序列的第一个字节>= 0x80，但之后的字节可能不是。
 * 如果我们直接扫描这样的序列，词法规则可能会轻易被愚弄，将这样的字节匹配到普通的ASCII字符。
 * 我们的解决方案是用0xFF替换在提供给flex的数据中的每个非首字节。
 * 然后，flex规则将无害地传递这些FF。psqlscan_emit()子例程负责回望原始字符串，并将FF替换为相应的原始字节。
 *
 * 我们在此处做的另一件有趣的事是使用物理上分开的flex词法分析器扫描同一输入的不同部分（即，写在单独.l文件中的词法分析器）。
 * 我们可以做到这一点，因为flex词法分析器持久状态中唯一依赖于其解析规则表的部分是起始状态编号，管理这一点相对容易——
 * 实际上，通常我们只需在更改词法分析器时将其设置为INITIAL。但是，要使其工作，我们必须使用重入词法分析器，
 * 确保所有相关状态在附加到PsqlScanState的yyscan_t中；
 * 如果使用具有单独静态状态的词法分析器，我们将很快遇到一个或另一个悬空的缓冲区指针。此外，如果词法分析器不是用相同的flex版本构建，
 * 或者它们没有使用相同的flex选项，这通常不会很好地工作。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/fe_utils/psqlscan_int.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PSQLSCAN_INT_H
#define PSQLSCAN_INT_H

#include "fe_utils/psqlscan.h"

/*
 * 这些只是为了允许此文件独立编译以进行头文件
 * 有效性检查；在实际使用中，此文件应始终从
 * flex 文件的主体中包含，这些符号已经定义。
 */
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
#endif

/*
 * 我们使用一个 flex 缓冲区的堆栈来处理 psql 变量的替换。
 * 每个堆叠的缓冲区包含来自一个 psql 变量的尚未读取的文本。
 * 当我们完全弹出堆栈时，我们从由 scanbufhandle 标识的
 * 外部缓冲区恢复读取。
 */
typedef struct StackElem
{
	YY_BUFFER_STATE buf;		/* flex 输入控制结构 */
	char	   *bufstring;		/* flex 实际扫描的数据 */
	char	   *origstring;		/* 原始数据的副本（如有需要） */
	char	   *varname;		/* 提供数据的变量名称，或 NULL */
	struct StackElem *next;
} StackElem;

/*
 * 分析器的所有工作状态必须在调用之间存储在 PsqlScanStateData 中。
 * 这使我们能够有多个打开的分析器操作，
 * 这对于嵌套的包含文件是必需的。分析器本身不是
 * 递归的，但它必须是可重入的。
 */
typedef struct PsqlScanStateData
{
	yyscan_t	scanner;		/* 此 PsqlScanState 的 Flex 状态 */

	PQExpBuffer output_buf;		/* 当前输出缓冲区 */

	StackElem  *buffer_stack;	/* 变量扩展缓冲区的堆栈 */

	/*
	 * 这些变量始终指向外部缓冲区，而不是任何堆叠的
	 * 变量扩展缓冲区。
	 */
	YY_BUFFER_STATE scanbufhandle;
	char	   *scanbuf;		/* 外层输入缓冲区的开始 */
	const char *scanline;		/* 外层当前输入行 */

	/* safe_encoding、curline、refline 由 emit() 用于替换 FFs */
	int			encoding;		/* 当前使用的编码 */
	bool		safe_encoding;	/* 当前编码“安全”吗？ */
	bool		std_strings;	/* 字符串文字是标准的吗？ */
	const char *curline;		/* 当前缓冲区的实际 flex 输入字符串 */
	const char *refline;		/* 当前缓冲区的原始数据 */

	/*
	 * 所有这些状态在连续输入行之间保持，直到被 psql_scan_reset 显式重置。
	 * start_state 在进入时由 yylex() 采用，
	 * 并在退出时以其完成状态更新。
	 */
	int			start_state;	/* yylex的起始/结束状态 */
	int			state_before_str_stop;	/* 结束引号之前的起始条件 */
	int			paren_depth;	/* 括号中的嵌套深度 */
	int			xcdepth;		/* 斜杠星号注释中的嵌套深度 */
	char	   *dolqstart;		/* 当前$foo$引号起始字符串 */

	/*
	 * 用于跟踪函数定义中BEGIN ... END块边界的状态，
	 * 以便分号不会过早发送查询。
	 */
	int			identifier_count;	/* 自语句开始以来的标识符 */
	char		identifiers[4]; /* 记录前几个标识符 */
	int			begin_depth;	/* 开始/结束对的深度 */

	/*
	 * 程序提供的回调函数，使用词法分析器，
	 * 以及一个void*回调传递参数。
	 */
	const PsqlScanCallbacks *callbacks;
	void	   *cb_passthrough;
} PsqlScanStateData;


/*
 * psqlscan.l导出的函数，但仅供兼容的词法分析器使用。
 */
extern void psqlscan_push_new_buffer(PsqlScanState state,
									 const char *newstr, const char *varname);
extern void psqlscan_pop_buffer_stack(PsqlScanState state);
extern void psqlscan_select_top_buffer(PsqlScanState state);
extern bool psqlscan_var_is_current_source(PsqlScanState state,
										   const char *varname);
extern YY_BUFFER_STATE psqlscan_prepare_buffer(PsqlScanState state,
											   const char *txt, int len,
											   char **txtcopy);
extern void psqlscan_emit(PsqlScanState state, const char *txt, int len);
extern char *psqlscan_extract_substring(PsqlScanState state,
										const char *txt, int len);
extern void psqlscan_escape_variable(PsqlScanState state,
									 const char *txt, int len,
									 PsqlScanQuoteType quote);
extern void psqlscan_test_variable(PsqlScanState state,
								   const char *txt, int len);

#endif							/* PSQLSCAN_INT_H */
